<!doctype html>
<html lang="en-us">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>CSS Transforms</title>
    <style>
        #allTransformFieldset {
            border: none;
            padding: 0;
            margin-bottom: 4px;
            accent-color: blue; /* or any color */
            font-family: Inter, "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
        }

        #allTransformFieldset>legend {
            margin-bottom: 4px;
        }

        fieldset {
            margin: 0;
        }

        legend {
            font-weight: bold;
            padding: 0;
        }

        #fieldsetSection {
            display: flex;
            flex-wrap: wrap;
            align-items: start;
            gap: 8px;
        }

        #outputSection {
            width: 100%;
            min-height: 400px;
            background: linear-gradient(skyblue, khaki);
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            overflow: clip;
        }

        #outputContainer {
            position: absolute;
            width: 100%;
            height: 100%;
            perspective: 200px;
            display: flex;
            justify-content: center;
            align-items: center;
            transform-style: preserve-3d;
            pointer-events: none;
        }

        #z0 {
            width: 50px;
            height: 50px;
            background: linear-gradient(to right bottom, rgb(223 223 223), rgb(190 190 190));
            transform: translateZ(0px);
            position: absolute;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 14px;
            color: black;
            border-radius: 50%;
            outline: 1px solid rgb(0 0 0 / 0.35);
            pointer-events: all;
        }

        #perspectiveDot {
            width: 4px;
            height: 4px;
            border-radius: 50%;
            background-color: rgb(240 0 0 / 0.5);
            transform: translate3d(-2px, -2px, 0px);
            position: absolute;
        }

        #cube {
            width: 100px;
            height: 100px;
            transform-style: preserve-3d;
            transition: all 0.075s ease-out;
            position: absolute;
            pointer-events: all;
        }

        .face {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
            position: absolute;
            backface-visibility: inherit;
            font-size: 60px;
            color: white;
        }

        .front {
            background: rgb(90 90 90 / 0.7);
            transform: translateZ(50px);
        }

        .back {
            background: rgb(0 210 0 / 0.7);
            transform: rotateY(180deg) translateZ(50px);
        }

        .right {
            background: rgb(210 0 0 / 0.7);
            transform: rotateY(90deg) translateZ(50px);
        }

        .left {
            background: rgb(0 0 210 / 0.7);
            transform: rotateY(-90deg) translateZ(50px);
        }

        .top {
            background: rgb(210 210 0 / 0.7);
            transform: rotateX(90deg) translateZ(50px);
        }

        .bottom {
            background: rgb(210 0 210 / 0.7);
            transform: rotateX(-90deg) translateZ(50px);
        }

        .transformFieldset {
            margin: 0;
        }

        .controlsContainer {
            display: flex;
            flex-direction: column;
            align-items: start;
            gap: 4px;
        }

        .controlsContainer {
            width: 100%;
        }

        .controlsContainer>div {
            display: flex;
            flex-direction: row;
            align-items: center;
            gap: 4px;
        }

        .controlsContainer>div>label {
            display: flex;
            flex-direction: row;
            align-items: center;
            gap: 4px;
        }

        button {
            font-size: 18px;
            border-radius: 50%;
            border: #ccc solid 1px;
            padding: 0;
            width: 26px;
            height: 26px;
            margin-left: 4px;
        }

        input[type='range'] {
            width: 172px;
        }

        output {
            width: 3em;
        }
    </style>
</head>

<body>
    <article>
        <fieldset id="allTransformFieldset">
            <legend>Transform settings
                <button id="resetAllButton" aria-label="Reset">↻</button>
            </legend>
            <section id="fieldsetSection">
                <fieldset class="transformFieldset">
                    <legend>
                        <label>
                            <span>Translation</span>
                        </label>
                        <button id="translateSectionReset" class="resetSectionButton" aria-label="Reset">↻</button>
                    </legend>
                    <div class="controlsContainer">
                        <div>
                            <label>
                                <span id="translateXLabel">X</span>
                                <input type="range" min="-100" max="100" value="0" data-default="0" id="translateXRange" aria-labelledby="translateXLabel" />
                            </label>
                            <output id="translateXOutput" for="translateXRange"></output>
                        </div>
                        <div>
                            <label>
                                <span id="translateYLabel">Y</span>
                                <input type="range" min="-100" max="100" value="0" data-default="0" id="translateYRange" aria-labelledby="translateYLabel" />
                            </label>
                            <output id="translateYOutput" for="translateYRange"></output>
                        </div>
                        <div>
                            <label>
                                <span id="translateZLabel">Z</span>
                                <input type="range" min="-100" max="100" value="0" data-default="0" id="translateZRange" aria-labelledby="translateZLabel" />
                            </label>
                            <output id="translateZOutput" for="translateZRange"></output>
                        </div>
                    </div>
                </fieldset>

                <fieldset class="transformFieldset">
                    <legend>
                        <label>
                            <span>Rotation</span>
                        </label>
                        <button id="rotateSectionReset" class="resetSectionButton" aria-label="Reset">↻</button>
                    </legend>
                    <div class="controlsContainer">
                        <div>
                            <label>
                                <span id="rotateXLabel">X</span>
                                <input type="range" min="-360" max="360" value="0" data-default="0" id="rotateXRange" aria-labelledby="rotateXLabel" />
                            </label>
                            <output id="rotateXOutput" for="rotateXRange"></output>
                        </div>
                        <div>
                            <label>
                                <span id="rotateYLabel">Y</span>
                                <input type="range" min="-360" max="360" value="0" data-default="0" id="rotateYRange" aria-labelledby="rotateYLabel" />
                            </label>
                            <output id="rotateYOutput" for="rotateYRange"></output>
                        </div>
                        <div>
                            <label>
                                <span id="rotateZLabel">Z</span>
                                <input type="range" min="-360" max="360" value="0" data-default="0" id="rotateZRange" aria-labelledby="rotateZLabel" />
                            </label>
                            <output id="rotateZOutput" for="rotateZRange"></output>
                        </div>
                    </div>
                </fieldset>

                <fieldset class="transformFieldset">
                    <legend>
                        <label>
                            <span>Scale</span>
                        </label>
                        <button id="scaleSectionReset" class="resetSectionButton" aria-label="Reset">↻</button>
                    </legend>
                    <div class="controlsContainer">
                        <div>
                            <label>
                                <span id="scaleXLabel">X</span>
                                <input type="range" min="0.1" max="2.5" value="1" data-default="1" step="0.1" id="scaleXRange" aria-labelledby="scaleXLabel" />
                            </label>
                            <output id="scaleXOutput" for="scaleXRange"></output>
                        </div>
                        <div>
                            <label id="scaleYLabel">
                                <span>Y</span>
                                <input type="range" min="0.1" max="2.5" value="1" data-default="1" step="0.1" id="scaleYRange" aria-labelledby="scaleYLabel" />
                            </label>
                            <output id="scaleYOutput" for="scaleYRange"></output>
                        </div>
                        <div>
                            <label id="scaleZLabel">
                                <span>Z</span>
                                <input type="range" min="0.1" max="2.5" value="1" data-default="1" step="0.1" id="scaleZRange" aria-labelledby="scaleZLabel" />
                            </label>
                            <output id="scaleZOutput" for="scaleZRange"></output>
                        </div>
                    </div>
                </fieldset>

                <fieldset class="transformFieldset">
                    <legend>
                        <label>
                            <span>Skew</span>
                        </label>
                        <button id="skewSectionReset" class="resetSectionButton" aria-label="Reset">↻</button>
                    </legend>
                    <div class="controlsContainer">
                        <div>
                            <label>
                                <span id="skewXLabel">X</span>
                                <input type="range" min="-90" max="90" value="0" data-default="0" id="skewXRange" aria-labelledby="skewXLabel" />
                            </label>
                            <output id="skewXOutput" for="skewXRange"></output>
                        </div>
                        <div>
                            <label>
                                <span id="skewYLabel">Y</span>
                                <input type="range" min="-90" max="90" value="0" data-default="0" id="skewYRange" aria-labelledby="skewYLabel" />
                            </label>
                            <output id="skewYOutput" for="skewYRange"></output>
                        </div>
                    </div>
                </fieldset>

                <fieldset class="transformFieldset">
                    <legend>
                        <label>
                            <span>Container Perspective</span>
                        </label>
                        <button id="containerPerspectiveSectionReset" class="resetSectionButton" aria-label="Reset">↻</button>
                    </legend>
                    <div class="controlsContainer">
                        <div>
                            <label>
                                <span id="perspectiveLabel"><code>perspective</code></span>
                                <input type="range" min="75" max="500" value="200" data-default="200" id="perspectiveRange" aria-labelledby="perspectiveLabel" />
                            </label>
                            <output id="perspectiveOutput" for="perspectiveRange"></output>
                        </div>
                        <div>
                            <label>
                                <span id="perspectiveOriginXLabel"><code>perspective-origin</code> X (%)</span>
                                <input type="range" min="0" max="100" value="50" data-default="50" id="perspectiveOriginXRange" aria-labelledby="perspectiveOriginXLabel" />
                            </label>
                            <output id="perspectiveOriginXOutput" for="perspectiveOriginXRange"></output>
                        </div>
                        <div>
                            <label>
                                <span><code>perspective-origin</code> Y (%)</span>
                                <input type="range" min="0" max="100" value="50" data-default="50" id="perspectiveOriginYRange" aria-labelledby="perspectiveOriginYLabel" />
                            </label>
                            <output id="perspectiveOriginYOutput" for="perspectiveOriginYRange"></output>
                        </div>
                    </div>
                </fieldset>

                <fieldset class="transformFieldset">
                    <legend>
                        <label>
                            <span>Other Properties</span>
                        </label>
                        <button id="otherSectionReset" class="resetSectionButton" aria-label="Reset">↻</button>
                    </legend>
                    <div class="controlsContainer">
                        <div>
                            <label>
                                <span><code>backface-visibility</code></span>
                                <input type="checkbox" checked="checked" data-default="checked" id="backfaceVisibilityCheckbox" />
                            </label>
                        </div>
                    </div>
                </fieldset>
            </section>
        </fieldset>
        <section id="outputSection">
            <div id="outputContainer">
                <div id="cube">
                    <div class="face front">1</div>
                    <div class="face back">2</div>
                    <div class="face right">3</div>
                    <div class="face left">4</div>
                    <div class="face top">5</div>
                    <div class="face bottom">6</div>
                </div>
                <div id="z0"><code>z:0px</code></div>
            </div>
            <div id="perspectiveDot"></div>
        </section>
    </article>
    <script>
        allTransformFieldset.querySelectorAll("input[type='range']").forEach((rangeInput) => {
            // Event listeners for when the range inputs change
            rangeInput.addEventListener("input", (el) => {
                updateTransform();
            });

            // Reset the relevant transform setting when the range input is double clicked
            rangeInput.addEventListener("dblclick", (el) => {
                resetInput(el.target);
                updateTransform();
            });
        });

        // Event listeners for when checkbox inputs change
        allTransformFieldset.querySelectorAll("input[type='checkbox']").forEach((checkboxInput) => {
            checkboxInput.addEventListener("input", (el) => {
                updateTransform();
            });
        });

        // "Reset All" button event listener
        resetAllButton.addEventListener("click", () => {
            allTransformFieldset.querySelectorAll('input').forEach((input) => {
                resetInput(input);
            });
            updateTransform();
        });

        // Section reset button event listeners
        allTransformFieldset.querySelectorAll(".resetSectionButton").forEach((resetSectionButton) => {
            resetSectionButton.addEventListener("click", (el) => {
                let allRanges = el.target.parentElement.parentElement.querySelectorAll("input[type='range']");
                allRanges.forEach((range) => {
                    resetInput(range);
                });

                let allCheckboxes = el.target.parentElement.parentElement.querySelectorAll("input[type='checkbox']");
                allCheckboxes.forEach((check) => {
                    resetInput(check);
                });

                updateTransform();
            });
        });

        const resetInput = (inputEl) => {
            if (!inputEl) {
                console.warn(`inputEl \`${inputEl}\` is falsey!`);
                console.trace();
                return;
            }

            const defaultValue = inputEl.getAttribute("data-default");
            if (inputEl.getAttribute('type') === 'checkbox') {
                inputEl.checked = defaultValue === "checked";
            } else {
                inputEl.value = defaultValue || "0";
            }
        }

        const updateOutputs = () => {
            translateXOutput.value = `${parseInt(translateXRange.value)}px`;
            translateYOutput.value = `${parseInt(translateYRange.value)}px`;
            translateZOutput.value = `${parseInt(translateZRange.value)}px`;

            rotateXOutput.value = `${parseInt(rotateXRange.value)}°`;
            rotateYOutput.value = `${parseInt(rotateYRange.value)}°`;
            rotateZOutput.value = `${parseInt(rotateZRange.value)}°`;

            scaleXOutput.value = `${parseFloat(scaleXRange.value)}x`;
            scaleYOutput.value = `${parseFloat(scaleYRange.value)}x`;
            scaleZOutput.value = `${parseFloat(scaleZRange.value)}x`;

            skewXOutput.value = `${parseFloat(skewXRange.value)}°`;
            skewYOutput.value = `${parseFloat(skewYRange.value)}°`;

            perspectiveOutput.value = `${parseInt(perspectiveRange.value)}px`;

            perspectiveOriginXOutput.value = `${parseInt(perspectiveOriginXRange.value)}%`;
            perspectiveOriginYOutput.value = `${parseInt(perspectiveOriginYRange.value)}%`;
        }

        const updateTransform = () => {
            updateOutputs();

            cube.style.transform = `translate3d(${translateXRange.value}px,
                ${translateYRange.value}px,
                ${translateZRange.value}px)
                rotateX(${rotateXRange.value}deg)
                rotateY(${rotateYRange.value}deg)
                rotateZ(${rotateZRange.value}deg)
                scale3d(${scaleXRange.value},
                ${scaleYRange.value},
                ${scaleZRange.value})
                skewX(${skewXRange.value}deg)
                skewY(${skewYRange.value}deg)`;
            cube.style.backfaceVisibility = `${backfaceVisibilityCheckbox.checked ? 'visible' : 'hidden'}`;

            outputContainer.style.perspective = `${perspectiveRange.value}px`;
            outputContainer.style.perspectiveOrigin = `${perspectiveOriginXRange.value}% ${perspectiveOriginYRange.value}%`;

            perspectiveDot.style.top = `${perspectiveOriginYRange.value}%`;
            perspectiveDot.style.left = `${perspectiveOriginXRange.value}%`;
        }
        updateTransform();
    </script>
</body>

</html>
